﻿using System;
using System.Reflection;
using System.Collections.Generic;

namespace NazTek.Extension.Clr4
{
    /// <summary> 
    /// Compares two instances of a type
    /// </summary> 
    /// <typeparam name="T">The type of objects to compare</typeparam>
    /// <remarks>The type must be a reference type</remarks> 
    public sealed class EqualityComparer<T> : IEqualityComparer<T> where T : class
    {
        /// <summary> 
        /// Reflectively compares the readable non-indexed properties of the two provided reference type instances 
        /// </summary> 
        /// <param name="x">First instance to compare</param> 
        /// <param name="y">Second instance to compare with the first</param> 
        /// <returns>True when all property values of both the instances are equal</returns> 
        /// <remarks></remarks> 
        /// <example>
        /// <code>
        /// bool b = new EqualityComparer&lt;MyType&gt;().Equals(MyTypeInstance1, MyTypeInstance2);
        /// </code>
        /// </example>
        public bool Equals(T x, T y)
        {
            Type oType = typeof(T);
            foreach (PropertyInfo pi in oType.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
            {
                {
                    if (pi.CanRead && pi.GetIndexParameters().Length == 0 && (pi.PropertyType.IsValueType || object.ReferenceEquals(pi.PropertyType, typeof(string))))
                    {
                        object oX = pi.GetValue(x, null);
                        object oY = pi.GetValue(y, null);
                        if (oX != null)
                        {
                            if (!oX.Equals(oY) || oY == null)
                                return false;
                        }
                        else
                            if (oY != null) return false;
                    }
                }
            }
            return true;
        }
        /// <summary> 
        /// Gets the hash code by delegating the call to that of the provided instance 
        /// </summary> 
        /// <param name="obj">The object instance that provides the actual implementation</param> 
        /// <returns>The hash code</returns> 
        /// <remarks>Object is serialized using the <see cref="FormatOption.Pox"/> formatting option and it's hash code is returned</remarks> 
        public int GetHashCode(T obj)
        {
            return obj.ToString(FormatOption.Pox).GetHashCode();
        }
    }
}